#include "detection.h"
#include <opencv2/core.hpp>
#include <opencv2/objdetect.hpp>

#include <string>
#include <vector>

#include <android/log.h>

#define LOG_TAG "FaceDetection/DetectionBasedTracker"
#define LogD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

using namespace std;
using namespace cv;


inline void vector_Rect_to_Mat(vector<Rect> &v_rect, Mat &mat) {
    mat = Mat(v_rect, true);
}


class CascadeDetectorAdapter : public DetectionBasedTracker::IDetector {

public:
    CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector) :
            IDetector(),
            Detector(detector) {
        LogD("CascadeDetectorAdapter::Detect::Detect");
        CV_Assert(detector);
    }

    void detect(const cv::Mat &Image, std::vector<cv::Rect> &objects) {
        LogD("CascadeDetectorAdapter::Detect: begin");
        LogD("CascadeDetectorAdapter::Detect: scaleFactor=%.2f, minNeighbours=%d, minObjSize=(%dx%d), maxObjSize=(%dx%d)",
             scaleFactor, minNeighbours, minObjSize.width, minObjSize.height, maxObjSize.width,
             maxObjSize.height);
        Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours, 0, minObjSize,
                                   maxObjSize);
        LogD("CascadeDetectorAdapter::Detect: end");
    }

    virtual ~CascadeDetectorAdapter() {
        LogD("CascadeDetectorAdapter::Detect::~Detect");
    }

private:
    CascadeDetectorAdapter();

    cv::Ptr<cv::CascadeClassifier> Detector;
};

struct DetectorAgReGator {

    cv::Ptr<CascadeDetectorAdapter> mainDetector;
    cv::Ptr<CascadeDetectorAdapter> trackingDetector;

    cv::Ptr<DetectionBasedTracker> tracker;

    DetectorAgReGator(cv::Ptr<CascadeDetectorAdapter> &_mainDetector,
                      cv::Ptr<CascadeDetectorAdapter> &_trackingDetector) :
            mainDetector(_mainDetector),
            trackingDetector(_trackingDetector) {
        CV_Assert(_mainDetector);
        CV_Assert(_trackingDetector);

        DetectionBasedTracker::Parameters DetectorParams;
        tracker = makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);
    }
};

JNIEXPORT jlong JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeCreateObject(JNIEnv *env,
                                                                                 jclass type,
                                                                                 jstring cascadeName_,
                                                                                 jint minFaceSize) {

    LogD("进入方法 nativeCreateObject");
    const char *j_name_str = env->GetStringUTFChars(cascadeName_, NULL);
    string std_file_name = j_name_str;
    jlong result = 0;
    LogD("开始执行方法 nativeCreateObject");
    try {
        cv::Ptr<CascadeDetectorAdapter> mainDetector = makePtr<CascadeDetectorAdapter>(
                makePtr<CascadeClassifier>(std_file_name));
        cv::Ptr<CascadeDetectorAdapter> trackingDetector = makePtr<CascadeDetectorAdapter>(
                makePtr<CascadeClassifier>(std_file_name));
        result = (jlong) new DetectorAgReGator(mainDetector, trackingDetector);
        if (minFaceSize > 0) {
            mainDetector->setMinObjectSize(Size(minFaceSize, minFaceSize));
        }
    }
    catch (cv::Exception &e) {
        LogD("方法nativeCreateObject 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法nativeCreateObject 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法nativeCreateObject 执行完毕");
    return result;
}

JNIEXPORT void JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeDestroyObject(JNIEnv *env,
                                                                                  jclass type,
                                                                                  jlong thiz) {


    LogD("开始执行方法 nativeDestroyObject");
    try {
        if (thiz != 0) {
            ((DetectorAgReGator *) thiz)->tracker->stop();
            delete (DetectorAgReGator *) thiz;
        }

    }
    catch (cv::Exception &e) {
        LogD("方法 nativeDestroyObject 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法 nativeCreateObject 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法 nativeDestroyObject 执行完毕");
}

JNIEXPORT void JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeStart(JNIEnv *env, jclass type,
                                                                          jlong thiz) {
    LogD("开始执行方法 nativeStart");
    try {
        ((DetectorAgReGator *) thiz)->tracker->run();
    }
    catch (cv::Exception &e) {
        LogD("方法 nativeStart 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法 nativeStart 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法 nativeStart 执行完毕");
}

JNIEXPORT void JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeStop(JNIEnv *env, jclass type,
                                                                         jlong thiz) {

    LogD("开始执行方法 nativeStop");
    try {

        ((DetectorAgReGator *) thiz)->tracker->stop();
    }
    catch (cv::Exception &e) {
        LogD("方法 nativeStop 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法nativeStop 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法 nativeStop 执行完毕");
}

JNIEXPORT void JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeSetFaceSize(JNIEnv *env,
                                                                                jclass type,
                                                                                jlong thiz,
                                                                                jint size) {

    LogD("开始执行方法 nativeSetFaceSize");
    try {

        ((DetectorAgReGator *) thiz)->mainDetector->setMinObjectSize(Size(size, size));
    }
    catch (cv::Exception &e) {
        LogD("方法nativeSetFaceSize 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法nativeSetFaceSize 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法 nativeSetFaceSize 执行结束");
}

JNIEXPORT void JNICALL
Java_com_jjkj_administrator_opencvdemo1_DetectionBasedTracker_nativeDetect(JNIEnv *env, jclass type,
                                                                           jlong thiz,
                                                                           jlong inputImage,
                                                                           jlong faces) {

    LogD("开始执行方法 nativeDetect");
    try {
        vector<Rect> RectFaces;

        ((DetectorAgReGator *) thiz)->tracker->process(*(Mat *) inputImage);
        ((DetectorAgReGator *) thiz)->tracker->getObjects(RectFaces);
        *((Mat *) faces) = Mat(RectFaces, true);
    }
    catch (cv::Exception &e) {
        LogD("方法 nativeDetect 出现异常 cv::Exception: %s", e.what());
        jclass j_e_clazz = env->FindClass("org/opencv/core/CvException");
        if (!j_e_clazz) {
            j_e_clazz = env->FindClass("java/lang/Exception");
        }
        env->ThrowNew(j_e_clazz, e.what());
    }
    catch (...) {
        LogD("方法 nativeDetect 出现未知异常");
        jclass j_e_clazz = env->FindClass("java/lang/Exception");
        env->ThrowNew(j_e_clazz, "未知原因异常");
    }
    LogD("方法 nativeDetect 执行结束");
}
